# -*- coding: utf-8 -*-
import os
import re
import codecs
import logging
from pathlib import Path

try:
    import markdown
    from markdown.extensions import codehilite, tables, toc
    MARKDOWN_AVAILABLE = True
except ImportError:
    MARKDOWN_AVAILABLE = False
    print("警告: markdown 库未安装，请运行 'pip install markdown' 安装。")

try:
    from reportlab.lib.pagesizes import A4
    from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
    from reportlab.lib.units import inch, cm
    from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, PageBreak
    from reportlab.platypus import Table, TableStyle
    from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY
    from reportlab.lib.colors import black, blue, red, green
    from reportlab.pdfbase import pdfmetrics
    from reportlab.pdfbase.ttfonts import TTFont
    PDF_AVAILABLE = True
except ImportError:
    PDF_AVAILABLE = False
    print("警告: reportlab 库未安装，PDF 输出功能不可用。")

try:
    from PIL import Image as PILImage
    PILLOW_AVAILABLE = True
except ImportError:
    PILLOW_AVAILABLE = False
    print("警告: Pillow 库未安装，图片处理功能不可用。")

logger = logging.getLogger(__name__)

class MarkdownHTMLParser:
    """自定义HTML解析器，用于将HTML转换为PDF元素"""
    
    def __init__(self, writer):
        self.writer = writer
        self.current_text = ""
        self.current_style = writer.normal_style
        
    def feed(self, html_content):
        """解析HTML内容"""
        from html import unescape
        
        # 确保输入是正确的字符串
        if isinstance(html_content, bytes):
            html_content = html_content.decode('utf-8')
        
        # 按行处理HTML内容
        lines = html_content.split('\n')
        
        for line in lines:
            line = line.strip()
            if not line:
                continue
            
            # 处理图片占位符
            if '<img_placeholder' in line:
                # 先添加当前累积的文本
                if self.current_text.strip():
                    self._add_paragraph()
                
                # 提取图片路径并添加图片
                img_match = re.search(r'<img_placeholder src="([^"]+)" />', line)
                if img_match:
                    img_path = img_match.group(1)
                    self.writer._add_image_to_story(img_path)
                continue
            
            # 处理标题
            if line.startswith('<h1>'):
                if self.current_text.strip():
                    self._add_paragraph()
                self.current_style = self.writer.h1_style
                clean_text = re.sub(r'<[^>]+>', '', line)
                self.current_text = unescape(clean_text)  # 添加HTML解码
                self._add_paragraph()
            elif line.startswith('<h2>'):
                if self.current_text.strip():
                    self._add_paragraph()
                self.current_style = self.writer.h2_style
                clean_text = re.sub(r'<[^>]+>', '', line)
                self.current_text = unescape(clean_text)  # 添加HTML解码
                self._add_paragraph()
            elif line.startswith('<h3>'):
                if self.current_text.strip():
                    self._add_paragraph()
                self.current_style = self.writer.h3_style
                clean_text = re.sub(r'<[^>]+>', '', line)
                self.current_text = unescape(clean_text)  # 添加HTML解码
                self._add_paragraph()
            elif line.startswith('<pre><code>') or line.startswith('<code>'):
                if self.current_text.strip():
                    self._add_paragraph()
                code_text = re.sub(r'<[^>]+>', '', line)
                code_text = unescape(code_text)  # 添加HTML解码
                if code_text.strip():
                    try:
                        para = Paragraph(code_text, self.writer.code_style)
                        self.writer.story.append(para)
                        self.writer.story.append(Spacer(1, 6))
                    except Exception as e:
                        logger.warning(f"添加代码段落失败: {e}")
            elif line.startswith('<blockquote>'):
                if self.current_text.strip():
                    self._add_paragraph()
                quote_text = re.sub(r'<[^>]+>', '', line)
                quote_text = unescape(quote_text)  # 添加HTML解码
                if quote_text.strip():
                    try:
                        para = Paragraph(quote_text, self.writer.quote_style)
                        self.writer.story.append(para)
                        self.writer.story.append(Spacer(1, 6))
                    except Exception as e:
                        logger.warning(f"添加引用段落失败: {e}")
            else:
                # 普通文本
                clean_text = re.sub(r'<[^>]+>', '', line)
                clean_text = unescape(clean_text)  # 添加HTML解码
                if clean_text.strip():
                    if self.current_text:
                        self.current_text += ' ' + clean_text
                    else:
                        self.current_text = clean_text
                        self.current_style = self.writer.normal_style
            # 每个段落处理完后，添加到story中
            if self.current_text.strip():
                self._add_paragraph()
        
        # 处理剩余文本
        if self.current_text.strip():
            self._add_paragraph()
    
    def _add_paragraph(self):
        """添加段落到story中"""
        # 移除这行调试代码：print("current_text:",self.current_text)
        if self.current_text.strip():
            try:
                # 确保文本是UTF-8编码
                text = self.current_text.strip()
                if isinstance(text, bytes):
                    text = text.decode('utf-8')
                
                # 确保文本是正确的Unicode字符串
                if not isinstance(text, str):
                    text = str(text)
                
                para = Paragraph(text, self.current_style)
                self.writer.story.append(para)
                self.writer.story.append(Spacer(1, 6))
                logger.info(f"成功添加段落: {text[:30]}...")
                
            except Exception as e:
                logger.warning(f"添加段落失败: {e}, 文本: {self.current_text[:50]}...")
                # 尝试使用默认字体
                try:
                    fallback_style = ParagraphStyle(
                        'Fallback',
                        fontName='Helvetica',
                        fontSize=self.current_style.fontSize,
                        spaceAfter=6
                    )
                    text = self.current_text.strip()
                    if isinstance(text, bytes):
                        text = text.decode('utf-8')
                    
                    # 确保文本是正确的Unicode字符串
                    if not isinstance(text, str):
                        text = str(text)
                    
                    para = Paragraph(text, fallback_style)
                    self.writer.story.append(para)
                    self.writer.story.append(Spacer(1, 6))
                    logger.info("使用降级字体成功")
                except Exception as e2:
                    logger.error(f"使用降级字体也失败: {e2}")
            finally:
                self.current_text = ""
                self.current_style = self.writer.normal_style


class MarkdownToPDFWriter:
    """Markdown转PDF写入器"""
    
    def __init__(self, font_name=None, font_size=None, page_size=None,
                 margin_top=None, margin_bottom=None, margin_left=None, margin_right=None):
        if not PDF_AVAILABLE:
            raise ImportError("reportlab 库未安装，无法使用 PDF 输出功能")
        if not MARKDOWN_AVAILABLE:
            raise ImportError("markdown 库未安装，无法使用 Markdown 解析功能")
            
       
        # 默认设置
        self.font_name = font_name or 'Helvetica'
        self.font_size = font_size or 12
        self.page_size = page_size or A4
        self.margin_top = margin_top or 2*cm
        self.margin_bottom = margin_bottom or 2*cm
        self.margin_left = margin_left or 2*cm
        self.margin_right = margin_right or 2*cm
        
        # 先注册字体（这会更新 self.font_name 为 'CustomFont'）
        self._register_fonts()
        
        # 初始化样式（现在使用正确的字体名称）
        self.styles = getSampleStyleSheet()
        self._setup_styles()
        
        # 存储文档内容
        self.story = []
        self.title = None
        
        # 初始化markdown解析器
        self.md = markdown.Markdown(extensions=[
            'codehilite',
            'tables', 
            'toc',
            'fenced_code',
            'nl2br'
        ])
    
    def _register_fonts(self):
        """注册中文字体"""
        try:
            import platform
            system = platform.system()
            font_registered = False
            
            if system == "Darwin":  # macOS
                font_paths = [
                    "/System/Library/Fonts/STHeiti Light.ttc",
                    "/System/Library/Fonts/PingFang.ttc",
                    "/Library/Fonts/Arial Unicode MS.ttf"
                ]
            elif system == "Windows":
                font_paths = [
                    "C:/Windows/Fonts/simsun.ttc",
                    "C:/Windows/Fonts/simhei.ttf",
                    "C:/Windows/Fonts/msyh.ttc"
                ]
            else:  # Linux
                font_paths = [
                    "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
                    "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf"
                ]
                
            for font_path in font_paths:
                if os.path.exists(font_path):
                    try:
                        # 使用统一的字体名称
                        pdfmetrics.registerFont(TTFont('CustomFont', font_path))
                        self.font_name = 'CustomFont'
                        print(f"成功注册字体: CustomFont from {font_path}")
                        logger.info(f"成功注册字体: CustomFont from {font_path}")
                        
                        # 更严格的字体验证
                        test_style = ParagraphStyle(
                            'TestStyle',
                            fontName='CustomFont',
                            fontSize=12
                        )
                        
                        # 测试多种中文字符
                        test_texts = [
                            "测试中文字体",
                            "简体中文测试",
                            "Hello 中文 World",
                            "数字123中文测试"
                        ]
                        
                        for test_text in test_texts:
                            try:
                                test_para = Paragraph(test_text, test_style)
                                logger.info(f"字体验证成功: {test_text}")
                            except Exception as e:
                                logger.warning(f"字体验证失败: {test_text} - {e}")
                                raise e
                        
                        font_registered = True
                        break
                        
                    except Exception as e:
                        logger.warning(f"注册字体失败 {font_path}: {e}")
                        continue
                    
            if not font_registered:
                logger.warning("未找到合适的中文字体，使用默认字体")
                self.font_name = 'Helvetica'
                
        except Exception as e:
            logger.warning(f"字体注册过程出错: {e}")
            self.font_name = 'Helvetica'

    def _process_images(self, html_content, markdown_file_path):
        """处理HTML中的图片标签，使用占位符保持位置"""
        if not PILLOW_AVAILABLE:
            return html_content
            
        # 获取markdown文件所在目录
        base_dir = os.path.dirname(os.path.abspath(markdown_file_path))
        
        # 查找所有图片标签，替换为占位符
        img_pattern = r'<img[^>]*src=["\']([^"\'>]+)["\'][^>]*>'
        
        def replace_img(match):
            img_src = match.group(1)
            
            # 处理相对路径
            if not os.path.isabs(img_src):
                img_path = os.path.join(base_dir, img_src)
            else:
                img_path = img_src
            
            # 检查图片文件是否存在
            if os.path.exists(img_path):
                # 返回占位符而不是立即添加图片
                return f'<img_placeholder src="{img_path}" />'
            else:
                logger.warning(f"图片文件不存在: {img_path}")
                return f"[图片不存在: {img_src}]"
        
        return re.sub(img_pattern, replace_img, html_content)
    
    def _setup_styles(self):
        """设置PDF样式"""
        styles = getSampleStyleSheet()
        
        # 正常段落样式 - 使用注册的字体
        self.normal_style = ParagraphStyle(
            'CustomNormal',
            parent=styles['Normal'],
            fontName=self.font_name,  # 使用 CustomFont
            fontSize=self.font_size,
            spaceAfter=6,
            alignment=TA_JUSTIFY
        )
        
        # 一级标题样式 - 增加更大的字体差异
        self.h1_style = ParagraphStyle(
            'CustomHeading1',
            parent=styles['Heading1'],
            fontName=self.font_name,  # 使用 CustomFont
            fontSize=self.font_size + 10,  # 从 +6 改为 +10
            spaceAfter=12,
            spaceBefore=12,
            textColor=black
        )
        
        # 二级标题样式 - 增加字体差异
        self.h2_style = ParagraphStyle(
            'CustomHeading2',
            parent=styles['Heading2'],
            fontName=self.font_name,  # 使用 CustomFont
            fontSize=self.font_size + 6,  # 从 +4 改为 +6
            spaceAfter=10,
            spaceBefore=10,
            textColor=black
        )
        
        # 三级标题样式 - 增加字体差异
        self.h3_style = ParagraphStyle(
            'CustomHeading3',
            parent=styles['Heading3'],
            fontName=self.font_name,  # 使用 CustomFont
            fontSize=self.font_size + 3,  # 从 +2 改为 +3
            spaceAfter=8,
            spaceBefore=8,
            textColor=black
        )
        
        # 代码样式
        self.code_style = ParagraphStyle(
            'CustomCode',
            parent=styles['Code'],
            fontName=self.font_name,  # 使用 CustomFont 而不是 Courier
            fontSize=self.font_size - 1,
            spaceAfter=6,
            spaceBefore=6,
            leftIndent=20,
            backgroundColor=None,
            borderColor=black,
            borderWidth=1,
            borderPadding=5
        )
        
        # 引用样式
        self.quote_style = ParagraphStyle(
            'CustomQuote',
            parent=styles['Normal'],
            fontName=self.font_name,  # 使用 CustomFont
            fontSize=self.font_size,
            spaceAfter=6,
            spaceBefore=6,
            leftIndent=20,
            rightIndent=20,
            textColor=black,
            borderColor=black,
            borderWidth=0,
            borderPadding=10
        )
        
        # 标题样式 - 增加字体差异
        self.title_style = ParagraphStyle(
            'CustomTitle',
            parent=styles['Title'],
            fontName=self.font_name,  # 使用 CustomFont
            fontSize=self.font_size + 12,  # 从 +8 改为 +12
            spaceAfter=20,
            spaceBefore=20,
            alignment=TA_CENTER,
            textColor=black
        )
    def _add_image_to_story(self, img_path):
        """添加图片到PDF文档中"""
        try:
            # 检查图片文件是否存在
            if not os.path.exists(img_path):
                logger.warning(f"图片文件不存在: {img_path}")
                error_para = Paragraph(f"[图片文件不存在: {os.path.basename(img_path)}]", self.normal_style)
                self.story.append(error_para)
                return
                
            # 获取图片信息
            with PILImage.open(img_path) as pil_img:
                img_width, img_height = pil_img.size
            
            # 计算可用页面宽度（留出更多边距）
            available_width = self.page_size[0] - self.margin_left - self.margin_right - 20  # 额外留20点边距
            available_height = self.page_size[1] - self.margin_top - self.margin_bottom - 40  # 额外留40点边距
            
            # 设置最大图片尺寸限制
            max_width = min(available_width, 400)  # 最大宽度400点
            max_height = min(available_height, 500)  # 最大高度500点
            
            # 计算缩放比例
            scale_width = max_width / img_width
            scale_height = max_height / img_height
            scale = min(scale_width, scale_height, 0.8)  # 最大缩放到80%，确保不会太大
            
            # 计算最终尺寸
            final_width = img_width * scale
            final_height = img_height * scale
            
            # 确保最终尺寸不超过限制
            if final_width > max_width:
                final_width = max_width
                final_height = (final_width / img_width) * img_height
            if final_height > max_height:
                final_height = max_height
                final_width = (final_height / img_height) * img_width
            
            logger.info(f"图片 {os.path.basename(img_path)}: 原始尺寸 {img_width}x{img_height}, 缩放后 {final_width:.1f}x{final_height:.1f}")
            
            # 创建图片对象
            img = Image(img_path, width=final_width, height=final_height)
            
            # 添加到story中
            self.story.append(Spacer(1, 10))
            self.story.append(img)
            self.story.append(Spacer(1, 10))
            
        except Exception as e:
            logger.error(f"添加图片失败 {img_path}: {e}")
            # 添加错误提示文本
            error_para = Paragraph(f"[图片加载失败: {os.path.basename(img_path)} - {str(e)}]", self.normal_style)
            self.story.append(error_para)
    

    def _html_to_story(self, html_content):
        """将HTML内容转换为PDF story元素"""
        try:
            # 使用HTML解析器处理内容
            parser = MarkdownHTMLParser(self)
            parser.feed(html_content)        
        except Exception as e:
            logger.error(f"HTML解析失败: {e}")
            # 降级到简单文本处理
            self._fallback_text_processing(html_content)
                                        
    def _fallback_text_processing(self, html_content):
        """降级文本处理方法"""
        # 移除所有HTML标签，保留纯文本
        text = re.sub(r'<[^>]+>', '', html_content)
        
        # 按段落分割
        paragraphs = text.split('\n\n')
        
        for para_text in paragraphs:
            para_text = para_text.strip()
            if para_text:
                # 处理多行文本
                lines = para_text.split('\n')
                combined_text = ' '.join(line.strip() for line in lines if line.strip())
                
                if combined_text:
                    try:
                        para = Paragraph(combined_text, self.normal_style)
                        self.story.append(para)
                        self.story.append(Spacer(1, 6))
                    except Exception as e:
                        logger.warning(f"添加段落失败: {e}")
                        
    def write_markdown_file(self, markdown_file):
        """处理单个markdown文件"""
        try:
            with codecs.open(markdown_file, 'r', encoding='utf-8') as f:
                markdown_content = f.read()
            
            # 转换为HTML
            html_content = self.md.convert(markdown_content)

            # print(html_content)
            
            # 处理图片
            html_content = self._process_images(html_content, markdown_file)
            # print(html_content)
            # 转换为PDF story元素
            self._html_to_story(html_content)
            
            # 重置markdown解析器
            self.md.reset()
            
            return True
            
        except Exception as e:
            logger.error(f"处理markdown文件失败 {markdown_file}: {e}")
            return False
    
    def set_title(self, title):
        """设置文档标题"""
        self.title = title
        return self
    
    def save(self, output_file):
        """保存PDF文档"""
        try:
            # 创建输出目录
            output_path = Path(output_file)
            output_path.parent.mkdir(parents=True, exist_ok=True)
            
            # 创建PDF文档
            doc = SimpleDocTemplate(
                output_file,
                pagesize=self.page_size,
                topMargin=self.margin_top,
                bottomMargin=self.margin_bottom,
                leftMargin=self.margin_left,
                rightMargin=self.margin_right
            )
            
            # 如果有标题，添加到开头
            if self.title:
                # 确保标题使用正确的字体
                title_para = Paragraph(self.title, self.h1_style)  # 改用h1_style而不是title_style
                self.story.insert(0, title_para)
                self.story.insert(1, Spacer(1, 20))
            
            # 构建PDF
            doc.build(self.story)
            logger.info(f"PDF已保存到: {output_file}")
            return True
            
        except Exception as e:
            logger.error(f"保存PDF失败: {e}")
            return False


def markdown_to_pdf(markdown_file, output_file, title=None, **kwargs):
    """将markdown文件转换为PDF的便捷函数"""
    try:
        writer = MarkdownToPDFWriter(**kwargs)
        
        if title:
            writer.set_title(title)
        
        success = writer.write_markdown_file(markdown_file)
        if success:
            success = writer.save(output_file)
            
        return success
        
    except Exception as e:
        logger.error(f"Markdown转PDF失败: {e}")
        return False


if __name__ == '__main__':
    # 测试代码
    import sys
    
    if len(sys.argv) < 3:
        print("用法: python markdown_to_pdf.py <markdown_file> <output_pdf>")
        sys.exit(1)
    
    markdown_file = sys.argv[1]
    output_file = sys.argv[2]
    title = sys.argv[3] if len(sys.argv) > 3 else None
    
    success = markdown_to_pdf(markdown_file, output_file, title)
    
    if success:
        print(f"成功生成PDF: {output_file}")
    else:
        print("PDF生成失败")
        sys.exit(1)